package com.roadmap.biz.sns;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.hibernate.Hibernate;

import com.roadmap.biz.common.BaseManager;
import com.roadmap.biz.common.RdcQueryConst;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Friendship;
import com.roadmap.entities.Request;
import com.roadmap.entities.ext.RequestExt;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidStatusException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NotAuthorizedException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.RdcQuery;

/* manage requests for applying for a friendship.
 * */
public class RequestManager extends BaseManager {

	public RequestManager(String currentUser, String itemOwner) {
		super(currentUser, itemOwner, Request.class);
	}

	/** reject a request */
	@Override
	public void cancel(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {

		checkEntityIsValid(entity);

		Request request = (Request) entity;
		request.setStatus(BaseConst.REQUEST_STATUS_REJECTED);
		getDbUtil().update(request);
	}

	/**
	 * approve a request. entityId should be the dispatcher's id.
	 * 
	 * @throws InvalidStatusException
	 */
	@Override
	public BaseEntity complete(String entityId) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {

		if (entityId == null) {
			NullObjectException e = new NullObjectException();
			_log.error("the request's dispatcher id is null.", e);
			throw e;
		}

		Request request = retrieveRequestByTwoUsers(getCurrentUserId(),
				entityId);
		if (request == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error(String.format(
					"the request sent by dispatcher %s was not found.",
					entityId), e);
			throw e;
		}

		// check if there's already a relationship stored in the database. if
		// not, create a new record
		Friendship frd = retrieveFriendshipByTwoUsers(request.getId(), request
				.getRecipientId());
		if (frd != null) {
			return request;
		}

		// change the request to APPROVED status.
		request.setStatus(BaseConst.REQUEST_STATUS_APPROVED);
		Vector<BaseEntity> entities = new Vector<BaseEntity>();
		entities.add(request);

		// create a friendship. the request recipient becomes a friend of
		// the dispatcher.
		frd = new Friendship();
		frd.setId(request.getId());
		frd.setFriendId(request.getRecipientId());
		entities.add(frd);

		getDbUtil().saveOrUpdateEntities(entities);
		getNoticeManager().notifyForEntry(frd, ActionType.ACCEPT, null);

		return request;
	}

	/**
	 * send a request.
	 */
	@Override
	public void create(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {

		checkEntityIsValid(entity);

		Request request = (Request) entity;
		// check if the two user are friends already.
		Friendship frd = retrieveFriendshipByTwoUsers(request.getId(), request
				.getRecipientId());
		if (frd != null) {
			// they're friends
			String msg = String.format("%s and %s are already friends.",
					request.getId(), request.getRecipientId());
			throw new InvalidActionException(msg);
		}

		// query in the database, check if there's a record of the request. if
		// the request exists and its status is REJECTED or COMPLETED, update
		// the record.
		Request req = retrieveRequestByTwoUsers(request.getId(), request
				.getRecipientId());
		if (req == null) {
			getDbUtil().save(request);
		}
		else {
			// update the request
			req.setStatus(BaseConst.REQUEST_STATUS_NEW);
			req.setNote(request.getNote());
			req.setCreatedTime(request.getCreatedTime());
			getDbUtil().update(req);
		}
	}

	/** return a list of unread requests */
	@Override
	public List<RequestExt> getEntitiesOfOwner() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		RdcQuery query = getDbUtil().createNativeSQLQuery(
				RdcQueryConst.SQL_NEW_REQUESTS_WITH_NAME);
		query.addEntity("r", Request.class);
		query.setParameter("uid", getOwnerId());
		query.setParameter("state", BaseConst.REQUEST_STATUS_NEW);
		query.addScalar("realName", Hibernate.STRING);
		List<Object[]> results = query.getResults();
		if (results == null || results.isEmpty()) {
			return null;
		}

		List<RequestExt> requests = new ArrayList<RequestExt>();
		Iterator<Object[]> iter = results.iterator();
		while (iter.hasNext()) {
			Object[] result = (Object[]) iter.next();
			RequestExt ext = new RequestExt((Request) result[0]);
			ext.setRequester((String) result[1]);
			requests.add(ext);
		}

		return requests;
	}

	/** a request identified by the combination of two users ignoring the order */
	private Request retrieveRequestByTwoUsers(String userAId, String userBId)
			throws NullObjectException, InvalidQueryException, ServerException,
			InvalidValueException {
		if (userAId == null || userBId == null) {
			return null;
		}
		Object[] params = { userAId, userBId, userBId, userAId };
		return (Request) getDbUtil().getUniqueEntity(
				RdcQueryConst.HQL_REQUEST_BETWEEN_TWO_USERS, params);
	}

	private Friendship retrieveFriendshipByTwoUsers(String disId, String repId)
			throws ServerException {
		if (disId == null || repId == null) {
			return null;
		}
		Object[] params = { disId, repId, repId, disId };
		return (Friendship) getDbUtil().getUniqueEntity(
				RdcQueryConst.HQL_FRIENDSHIP_BETWEEN_TWO_USERS, params);
	}

	@Override
	public BaseEntity cancel(String entryId) throws NullObjectException,
			ServerException, InvalidValueException, EntityNotFoundException,
			InvalidActionException, NotAuthorizedException,
			ActionNotSupportedException {
		if (entryId == null) {
			NullObjectException e = new NullObjectException();
			_log.error("Request id is null.", e);
			throw e;
		}

		Request request = retrieveRequestByTwoUsers(getCurrentUserId(),
				entryId);
		if (request == null) {
			EntityNotFoundException e = new EntityNotFoundException();
			_log.error(String.format("Request was not found by %s and %s",
					getCurrentUserId(), entryId), e);
			throw e;
		}

		request.setStatus(BaseConst.REQUEST_STATUS_REJECTED);
		request.setUpdatedTime(new Date());
		getDbUtil().update(request);
		return request;
	}
}
